Completed
Push — dev ( 3403e5...41defb )
by Fike
30s
created

Transition.js ➔ ... ➔ executionOptions   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
nop 1
1
var SDK = require('@ama-team/voxengine-sdk')
2
var Slf4j = SDK.Logger.Slf4j
3
var CancellationToken = SDK.Concurrent.CancellationToken
4
var Future = SDK.Concurrent.Future
5
var Branch = require('./Transition/Branch').Branch
6
7
var statusFactory = function (id, successful) {
8
  var terminal = typeof successful === 'boolean'
9
  return {
10
    id: id,
11
    terminal: terminal,
12
    successful: terminal ? successful : null
13
  }
14
}
15
16
/**
17
 * @enum
18
 * @readonly
19
 */
20
var Status = {
21
  Idle: statusFactory('Idle'),
22
  Executing: statusFactory('Executing'),
23
  Aborting: statusFactory('Aborting'),
24
  /**
25
   * Transition has completed successfully
26
   */
27
  Executed: statusFactory('Executed', true),
28
  /**
29
   * Transition has been aborted successfully
30
   */
31
  Aborted: statusFactory('Aborted', true),
32
  /**
33
   * Transition has failed and thrown an error during normal execution
34
   */
35
  ExecutionFailure: statusFactory('ExecutionFailure', false),
36
  /**
37
   * Transition has been aborted and encountered an error during abort
38
   */
39
  AbortFailure: statusFactory('AbortFailure', false),
40
  /**
41
   * Transition has ended with error triggered by framework
42
   */
43
  Tripped: statusFactory('Tripped', false)
44
}
45
46
/**
47
 * @typedef {object} Transition~Options
48
 *
49
 * @property {TState} [origin]
50
 * @property {TState} target
51
 * @property {THints} hints
52
 * @property {LoggerOptions} [logger]
53
 * @property {IExecutor} executor
54
 */
55
56
/**
57
 * Represents transition from one state to another
58
 *
59
 * @param {Transition~Options} options
60
 *
61
 * @class
62
 */
63
function Transition (options) {
64
  var self = this
65
  if (!options || !options.target) {
66
    throw new Error('Target state not provided')
67
  }
68
  var loggerName = 'ama-team.vsf.execution.transition'
69
  var origin = options.origin
70
  var target = options.target
71
  var originId = (origin && origin.id) || null
72
  var hints = options.hints
73
  options.logger = options.logger || {}
74
  options.logger.mdc = options.logger.mdc || {}
75
  options.logger.mdc['origin'] = originId
76
  options.logger.mdc['target'] = target.id
77
  var logger = Slf4j.factory(options.logger, loggerName)
78
79
  var token = new CancellationToken()
80
  var completion = new Future()
81
  var status = Status.Idle
82
83
  var launchedAt = null
84
85
  /**
86
   * @param {Transition.Status} next
87
   */
88
  function setStatus (next) {
89
    logger.trace('Changing status from {} to {}', status.id, next.id)
90
    status = next
91
  }
92
93
  function executionOptions (name) {
94
    return {
95
      name: name,
96
      handler: target[name],
97
      logger: options.logger,
98
      executor: options.executor
99
    }
100
  }
101
102
  /**
103
   * Returns function that will generate ITransitionResult from provided
104
   * value.
105
   *
106
   * @param {Status} status
107
   * @return {Function}
108
   */
109
  function factory (status) {
110
    return function (value) {
111
      return {
112
        value: value,
113
        status: status,
114
        duration: (new Date()).getTime() - launchedAt.getTime()
115
      }
116
    }
117
  }
118
119
  /**
120
   * Completes transition with provided result, if
121
   *
122
   * @param {TTransitionResult} result
123
   * @param {Status} [requiredStatus]
124
   */
125
  function complete (result, requiredStatus) {
126
    if (requiredStatus && status !== requiredStatus) {
127
      return
128
    }
129
    setStatus(result.status)
130
    logger.debug('Transition has ended with status {} and value {} in {} ms',
131
      status.id, result.value, result.duration)
132
    completion.resolve(result)
133
  }
134
135
  function run () {
136
    if (status !== Status.Idle) {
137
      throw new Error('Tried to run ' + self + ' twice')
138
    }
139
    logger.debug('Running transition')
140
    launchedAt = new Date()
141
    setStatus(Status.Executing)
142
    var opts = executionOptions('transition')
143
    var execution = new Branch(opts)
144
    execution
145
      .run(originId, hints, token)
146
      .then(factory(Status.Executed), factory(Status.ExecutionFailure))
147
      .then(function (result) {
148
        var level = result.status.successful ? 'debug' : 'warn'
149
        var term = result.status.successful ? 'success' : 'error'
150
        logger[level]('Transition run has finished with {}: {}', term,
151
          result.value)
152
        complete(result, Status.Executing)
153
      })
154
    return completion
155
  }
156
157
  function abort () {
158
    if (status !== Status.Executing) {
159
      throw new Error('Tried to abort ' + self)
160
    }
161
    setStatus(Status.Aborting)
162
    var options = executionOptions('abort')
163
    var execution = new Branch(options)
164
    execution
165
      .run(originId, hints)
166
      .then(factory(Status.Aborted), factory(Status.AbortFailure))
167
      .then(function (result) {
168
        var level = result.status.successful ? 'debug' : 'warn'
169
        var term = result.status.successful ? 'success' : 'error'
170
        logger[level]('Transition abort has finished with {}: {}', term,
171
          result.value)
172
        complete(result, Status.Aborting)
173
      })
174
    return completion
175
  }
176
177
  this.run = run
178
179
  this.abort = abort
180
181
  this.getStatus = function () {
182
    return status
183
  }
184
185
  this.toString = function () {
186
    return 'Transition ' + originId + ' -> ' + target.id +
187
      ' (' + status.id + ')'
188
  }
189
190
  /**
191
   * @return {TTransitionDetails}
192
   */
193
  this.toDetails = function () {
194
    return {
195
      origin: originId,
196
      target: target.id,
197
      hints: hints
198
    }
199
  }
200
201
  /**
202
   * @return {int}
203
   */
204
  this.getLaunchedAt = function () { return launchedAt }
205
206
  /**
207
   * @return {TState}
208
   */
209
  this.getTarget = function () { return target }
210
211
  this.getOrigin = function () { return origin }
212
213
  this.getHints = function () { return hints }
214
215
  this.getCompletion = function () { return completion }
216
}
217
218
Transition.Status = Status
219
220
module.exports = {
221
  Transition: Transition
222
}
223